import cv2
import glfw
from OpenGL.GL import *
import numpy as np
import time

# 路径配置
video_path = "/Users/xclin/Desktop/1.mp4"
video_out_path = '/Users/xclin/Desktop/1_out.mp4'
start_time = time.time()

# 使用opencv打开视频文件
cap = cv2.VideoCapture(video_path)
# 获取视频的宽度和高度
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 获取视频的帧率
fps = cap.get(cv2.CAP_PROP_FPS)
out = cv2.VideoWriter(video_out_path, cv2.VideoWriter_fourcc(*'MJPG'), fps, (width, height))
# 初始化glfw
if not glfw.init():
    raise Exception("glfw can not be initialized!")

# 设置opengl窗口的属性，设置窗口为不可见，用于离屏渲染
glfw.window_hint(glfw.VISIBLE, False)
    # 设置OpenGL版本号
glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True)
glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3)

# 创建一个opengl窗口
window = glfw.create_window(640, 480, "glfw opengl window", None, None)
if not window:
    glfw.terminate()
    raise Exception("glfw window can not be created")

# 设置当前窗口上下文
glfw.make_context_current(window)

# 创建帧缓冲区对象 FBO(frame buffer object) 用于离屏渲染
fbo = glGenFramebuffers(1)
# 绑定帧缓冲区对象
glBindFramebuffer(GL_FRAMEBUFFER, fbo)
# 创建纹理对象用于渲染
texture = glGenTextures(1)
# 绑定纹理对象
glBindTexture(GL_TEXTURE_2D, texture)
# 设置纹理的数据存储，此时不传入任何数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, None)
# 将纹理数据存储到帧缓冲区对象中
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0)

# 检查帧缓存是否完整
status = glCheckFramebufferStatus(GL_FRAMEBUFFER)
if status != GL_FRAMEBUFFER_COMPLETE:
    raise Exception("Framebuffer is not complete!")

# 顶点数组
vertices = np.array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0], dtype=np.float32)

# 创建顶点缓冲对象
vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW)

# 创建顶点数组对象
vao = glGenVertexArrays(1)
glBindVertexArray(vao)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)

# 编译顶点着色器
vertex_shader_source = """
#version 330 core
layout (location = 0) in vec2 position;
out vec2 TexCoord;

void main()
{
    gl_Position = vec4(position, 0.0, 1.0);
    TexCoord = (position + 1.0) / 2.0;  // 将顶点坐标转换为纹理坐标
}
"""

vertex_shader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertex_shader, vertex_shader_source)
glCompileShader(vertex_shader)

# 编译片段着色器
fragment_shader_source = """
#version 330 core
in vec2 TexCoord;
out vec4 FragColor;

uniform sampler2D textureSampler;  // 纹理采样器

void main()
{
    // 从纹理中采样颜色
    FragColor = texture(textureSampler, TexCoord);
}
"""

fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragment_shader, fragment_shader_source)
glCompileShader(fragment_shader)

# 创建着色器程序
shader_program = glCreateProgram()
glAttachShader(shader_program, vertex_shader)
glAttachShader(shader_program, fragment_shader)
glLinkProgram(shader_program)
glUseProgram(shader_program)

# 创建纹理
gl_texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, gl_texture)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

# 设置纹理采样器 uniform
texture_sampler_location = glGetUniformLocation(shader_program, "textureSampler")
glUniform1i(texture_sampler_location, 0)  # 绑定到纹理单元 0

# 主循环
while not glfw.window_should_close(window):
    # 渲染
    # 获取当前帧
    ret, frame = cap.read()
    if not ret:
        break
    # 将当前帧转换为opengl纹理
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    # 设置视频帧为OpenGL纹理的数据
    glBindTexture(GL_TEXTURE_2D, gl_texture)
    # 将当前帧渲染到opengl纹理中
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, frame.tobytes())

    # 绑定帧缓冲，准备渲染
    glBindFramebuffer(GL_FRAMEBUFFER, fbo)
    # 设置视口大小
    glViewport(0, 0, width, height)
    # 清空颜色缓冲区
    glClear(GL_COLOR_BUFFER_BIT)

    # 使用着色器程序渲染矩形
    glUseProgram(shader_program)
    # 绑定顶点数组对象
    glBindVertexArray(vao)

    # 渲染矩形
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)

    # 捕获渲染帧
    glReadBuffer(GL_COLOR_ATTACHMENT0)
    pixels = glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE)

    # 将像素数据转换回OpenCV的图像格式
    image = np.frombuffer(pixels, dtype=np.uint8).reshape(height, width, 3)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    # 将渲染帧写入输出视频文件
    out.write(image)

    # 交换缓冲区并轮询IO事件
    glfw.swap_buffers(window)
    glfw.poll_events()

# 程序结束，释放资源
cap.release()
out.release()
cv2.destroyAllWindows()
glfw.terminate()

end_time = time.time()
elapsed_time = end_time - start_time
print(f"Elapsed time: {elapsed_time} seconds")
